/*
 * Copyright 2019 Flipkart Internet Pvt. Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flipkart.ohos.proteus.value;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Text;
import ohos.app.Context;
import ohos.utils.LruBuffer;

import com.flipkart.ohos.proteus.parser.ParseHelper;
import com.flipkart.ohos.proteus.toolbox.HashBiMap;

/**
 * <p>
 * Dimension is a type of {@link Value} which hosts dimensions.
 *
 * @author aditya.sharat
 */
public class Dimension extends Value {
    public static final Text.TextSizeType DIMENSION_UNIT_PX = Text.TextSizeType.PX;
    public static final Text.TextSizeType DIMENSION_UNIT_VP = Text.TextSizeType.VP;
    public static final Text.TextSizeType DIMENSION_UNIT_FP = Text.TextSizeType.FP;
    public static final Text.TextSizeType DIMENSION_UNIT_ENUM = DIMENSION_UNIT_PX;
    //public static final int DIMENSION_UNIT_PT = TypedValue.COMPLEX_UNIT_PT;
    //public static final int DIMENSION_UNIT_IN = TypedValue.COMPLEX_UNIT_IN;
    //public static final int DIMENSION_UNIT_MM = TypedValue.COMPLEX_UNIT_MM;

    public static final String MATCH_PARENT = "match_parent";
    public static final String FILL_PARENT = "fill_parent";
    public static final String match_content = "match_content";

    public static final String SUFFIX_PX = "px";
    public static final String SUFFIX_DP = "vp";
    public static final String SUFFIX_SP = "fp";
    public static final String SUFFIX_PT = "pt";
    public static final String SUFFIX_IN = "in";
    public static final String SUFFIX_MM = "mm";

    public static final HashBiMap<String, Integer> sDimensionsMap = new HashBiMap<>(3);
    public static final HashBiMap<String, Object> sDimensionsUnitsMap = new HashBiMap<>(6);
    public static final Dimension ZERO = new Dimension(0, DIMENSION_UNIT_PX);

    static {
        sDimensionsMap.put(FILL_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
        sDimensionsMap.put(MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
        sDimensionsMap.put(match_content, ComponentContainer.LayoutConfig.MATCH_CONTENT);

        sDimensionsUnitsMap.put(SUFFIX_PX, DIMENSION_UNIT_PX);
        sDimensionsUnitsMap.put(SUFFIX_DP, DIMENSION_UNIT_VP);
        sDimensionsUnitsMap.put(SUFFIX_SP, DIMENSION_UNIT_FP);
        //sDimensionsUnitsMap.put(SUFFIX_PT, DIMENSION_UNIT_PT);
        //sDimensionsUnitsMap.put(SUFFIX_IN, DIMENSION_UNIT_IN);
        //sDimensionsUnitsMap.put(SUFFIX_MM, DIMENSION_UNIT_MM);
    }

    public final double value;
    public Text.TextSizeType unit;

    private Dimension(float value, Text.TextSizeType unit) {
        this.value = value;
        this.unit = unit;
    }

    private Dimension(String dimension) {
        Integer parameter = sDimensionsMap.getValue(dimension);
        double value = 0;
        Text.TextSizeType unit = null;

        if (parameter != null) {
            value = parameter;
            unit = DIMENSION_UNIT_ENUM;
        } else {
            int length = dimension.length();
            if (length < 2) {
                value = 0;
                unit = DIMENSION_UNIT_PX;
            } else { // find the units and value by splitting at the second-last character of the dimension
                Object va = sDimensionsUnitsMap.getValue(dimension.substring(length - 2));
                if (va == null) {
                    value = 0;
                    unit = DIMENSION_UNIT_PX;
                } else if (va instanceof Integer) {
                    String stringValue = dimension.substring(0, length - 2);
                    value = ParseHelper.parseFloat(stringValue);
                    unit = getUnit((String) va);
                } else if (va instanceof Text.TextSizeType) {
                    String stringValue = dimension.substring(0, length - 2);
                    value = ParseHelper.parseFloat(stringValue);
                    unit = (Text.TextSizeType) va;
                } else {
                    throw new RuntimeException("unit error: " + dimension);
                }
            }
        }
        this.value = value;
        this.unit = unit;
    }

    private Text.TextSizeType getUnit(String str) {
        if (Text.TextSizeType.FP.name().equalsIgnoreCase(str)) {
            return Text.TextSizeType.FP;
        } else if (Text.TextSizeType.VP.name().equalsIgnoreCase(str)) {
            return Text.TextSizeType.VP;
        } else if (Text.TextSizeType.PX.name().equalsIgnoreCase(str)) {
            return Text.TextSizeType.PX;
        }
        return Text.TextSizeType.FP;
    }

    /**
     * This function returns a {@code Dimension} object holding the
     * value extracted from the specified {@code String}
     *
     * @param dimension the value to be parsed.
     * @return return
     */
    public static Dimension valueOf(String dimension) {
        if (null == dimension) {
            return ZERO;
        }
        Dimension d = DimensionCache.cache.get(dimension);
        if (null == d) {
            d = new Dimension(dimension);
            DimensionCache.cache.put(dimension, d);
        }
        return d;
    }

    public static float apply(String dimension, Context context) {
        return Dimension.valueOf(dimension).apply(context);
    }

    public float apply(Context context) {
        double result = 0;
        switch (unit) {
            case FP:
                result = AttrHelper.fp2px((float) value, context);
                break;
            case PX:
                result = value;
                break;
            case VP:
                result = AttrHelper.vp2px((float) value, context);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + unit);
        }
        return (float) result;
    }

    @Override
    public Value copy() {
        return this;
    }

    @Override
    public String toString() {
        final String value;
        if (this.value % 1 == 0) {
            value = String.valueOf((int) this.value);
        } else {
            value = String.valueOf(this.value);
        }
        final String unit;
        if (this.unit == DIMENSION_UNIT_ENUM) {
            return sDimensionsMap.getKey((int) this.value);
        } else {
            unit = sDimensionsUnitsMap.getKey(this.unit);
            return value + unit;
        }
    }

    private static class DimensionCache {
        static final LruBuffer<String, Dimension> cache = new LruBuffer<>(64);
    }
}
